#include "errorcorrector.h"

errorCorrector::errorCorrector()
{
}


vector<int> errorCorrector::correctDataBlocks(vector<int> blocksV, int version, int errorCollectionLevel, int dataCapacity)
{
    int numSucceededCorrections = 0;
    int numCorrectionFailures = 0;
    int dataBlocks[dataCapacity];

    int numLastCorrectionFailures;

    vector<int> result;

    int blocks[blocksV.size()];
    for (int i = 0; i < blocksV.size(); i++) {
        blocks[i] = blocksV.at(i);
    }

    int numErrorCollectionCode = getNumErrorCollectionCode(version, errorCollectionLevel);
    int numRSBlocks = getNumRSBlocks(version, errorCollectionLevel);

    int eccPerRSBlock = numErrorCollectionCode / numRSBlocks;

    if (numRSBlocks == 1) {
        RSdecode corrector;
        corrector.setNpar(eccPerRSBlock / 2);
        int ret = corrector.decode(blocks);
        if (ret > 0)
                numSucceededCorrections += ret;
         else if (ret < 0)
                numCorrectionFailures++;

        for (int i = 0; i < sizeof (blocks); i++) {
            result.push_back(blocks[i]);
        }

        return result;
   } else  { //we have to interleave data blocks because symbol has 2 or more RS blocks
        int numLongerRSBlocks = dataCapacity % numRSBlocks;
        if (numLongerRSBlocks == 0) { //symbol has only 1 type of RS block
            int lengthRSBlock = dataCapacity / numRSBlocks;
            int RSBlocks[numRSBlocks][lengthRSBlock];
            //obtain RS blocks
            for (int i = 0; i < numRSBlocks; i++) {
                for (int j = 0; j < lengthRSBlock; j++) {
                    RSBlocks[i][j] = blocks[j * numRSBlocks + i];
                }
      //canvas.println("eccPerRSBlock=" + eccPerRSBlock );
      RSdecode corrector;
      corrector.setNpar(eccPerRSBlock / 2);
      int ret = corrector.decode(RSBlocks[i]);
      if (ret > 0)
        numSucceededCorrections += ret;
      else if (ret < 0)
        numCorrectionFailures++;
            }
            //obtain only data part
            int p = 0;
            for (int i = 0; i < numRSBlocks; i++) {
                for (int j = 0; j < lengthRSBlock - eccPerRSBlock; j++) {
                    dataBlocks[p++] = RSBlocks[i][j];
                }
            }
        }
        else { //symbol has 2 types of RS blocks
            int lengthShorterRSBlock = dataCapacity / numRSBlocks;
            int lengthLongerRSBlock = dataCapacity / numRSBlocks + 1;
            int numShorterRSBlocks = numRSBlocks - numLongerRSBlocks;
            int shorterRSBlocks[numShorterRSBlocks][lengthShorterRSBlock];
            int longerRSBlocks[numLongerRSBlocks][lengthLongerRSBlock];
            for (int i = 0; i < numRSBlocks; i++) {
                if (i < numShorterRSBlocks) { //get shorter RS Block(s)
                    int mod = 0;
                    for (int j = 0; j < lengthShorterRSBlock; j++) {
                        if (j == lengthShorterRSBlock - eccPerRSBlock) mod = numLongerRSBlocks;
                        shorterRSBlocks[i][j] = blocks[j * numRSBlocks + i + mod];
                    }
       // canvas.println("eccPerRSBlock(shorter)=" + eccPerRSBlock );
        RSdecode corrector;
        corrector.setNpar(eccPerRSBlock / 2);
        int ret = corrector.decode(shorterRSBlocks[i]);
        if (ret > 0)
          numSucceededCorrections += ret;
        else if (ret < 0)
          numCorrectionFailures++;

                }
                else { 	//get longer RS Blocks
                    int mod = 0;
                    for (int j = 0; j < lengthLongerRSBlock; j++) {
                        if (j == lengthShorterRSBlock - eccPerRSBlock) mod = numShorterRSBlocks;
                        longerRSBlocks[i - numShorterRSBlocks][j] = blocks[j * numRSBlocks + i - mod];
                    }
       // canvas.println("eccPerRSBlock(longer)=" + eccPerRSBlock );
        RSdecode corrector;
        corrector.setNpar(eccPerRSBlock / 2);
        int ret = corrector.decode(longerRSBlocks[i - numShorterRSBlocks]);
        if (ret > 0)
          numSucceededCorrections += ret;
        else if (ret < 0)
          numCorrectionFailures++;
                }
            }
            int p = 0;
            for (int i = 0; i < numRSBlocks; i++) {
                if (i < numShorterRSBlocks) {
                    for (int j = 0; j < lengthShorterRSBlock - eccPerRSBlock; j++) {
                        dataBlocks[p++] = shorterRSBlocks[i][j];
                    }
                }
                else {
                    for (int j = 0; j < lengthLongerRSBlock - eccPerRSBlock; j++) {
                        dataBlocks[p++] = longerRSBlocks[i - numShorterRSBlocks][j];
                    }
                }
            }
        }
      //  if (numSucceededCorrections > 0)
       //     canvas.println(String.valueOf(numSucceededCorrections) + " data errors corrected successfully.");
      //  else
      //      canvas.println("No errors found.");
        numLastCorrectionFailures = numCorrectionFailures;

        for (int i = 0; i < sizeof (dataBlocks); i++) {
            result.push_back(dataBlocks[i]);
        }

        return result;
    }
}


int errorCorrector::getNumRSBlocks(int version, int errorCollectionLevel)
{
    int numRSBlocks[40][4] = {
        { 1,1,1,1 },{ 1,1,1,1 }, { 1,1,2,2 }, { 1,2,2,4 }, { 1,2,4,4 }, { 2,4,4,4 },
        { 2,4,6,5 }, { 2,4,6,6 }, { 2,5,8,8 }, { 4,5,8,8 }, { 4,5,8,11 }, { 4,8,10,11 }, { 4,9,12,16 },
        { 4,9,16,16 }, { 6,10,12,18 }, { 6,10,17,16 }, { 6,11,16,19 }, { 6,13,18,21 }, { 7,14,21,25 }, { 8,16,20,25 },
        { 8,17,23,25 }, { 9,17,23,34 }, { 9,18,25,30 }, { 10,20,27,32 }, { 12,21,29,35 }, {12,23,34,37 }, { 12,25,34,40 },
        { 13,26,35,42 }, { 14,28,38,45 }, { 15,29,40,48 }, { 16,31,43,51 }, { 17,33,45,54 }, { 18,35,48,57 }, { 19,37,51,60 },
        { 19,38,53,63 }, { 20,40,56,66 }, { 21,43,59,70 }, { 22,45,62,74 }, { 24,47,65,77 }, { 25,49,68,81 }
    };

    return numRSBlocks[version - 1][errorCollectionLevel];
}

int errorCorrector::getNumErrorCollectionCode(int version, int errorCollectionLevel)
{
    int numErrorCollectionCode[40][4] = {
        { 7,10,13,17 },
        { 10,16,22,28 }, { 15,26,36,44 }, { 20,36,52,64 }, { 26,48,72,88 }, { 36,64,96,112 },
        { 40,72,108,130 }, { 48,88,132,156 }, { 60,110,160,192 }, { 72,130,192,224 }, { 80,150,224,264 }, { 96,176,260,308 }, { 104,198,288,352 },
        { 120,216,320,384 }, { 132,240,360,432 }, { 144,280,408,480 }, { 168,308,448,532 }, { 180,338,504,588 }, { 196,364,546,650 }, { 224,416,600,700 },
        { 224,442,644,750 }, { 252,476,690,816 }, { 270,504,750,900 }, { 300,560,810,960 }, { 312,588,870,1050 }, { 336,644,952,1110 }, { 360,700,1020,1200 },
        { 390,728,1050,1260 }, { 420,784,1140,1350 }, { 450,812,1200,1440 }, { 480,868,1290,1530 }, { 510,924,1350,1620 }, { 540,980,1440,1710 }, { 570,1036,1530,1800 },
        { 570,1064,1590,1890 }, { 600,1120,1680,1980 }, { 630,1204,1770,2100 }, { 660,1260,1860,2220 }, { 720,1316,1950,2310 }, { 750,1372,2040,2430 }
    };

    return numErrorCollectionCode[version - 1][errorCollectionLevel];
}
